package logs

import (
	"fmt"
	"io"
	"log"
	"time"

	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	debugLogger *zap.Logger
	infoLogger  *zap.Logger
	warnLogger  *zap.Logger
	errorLogger *zap.Logger
	panicLogger *zap.Logger

	// 小时分割日志
	splitDate = time.Hour
	// 日志保存时间
	ageDate = time.Hour * 24
)

// 初始化多种日志记录器
func init() {
	initDebugLogger(defaultConfigOption())
	initInfoLogger(defaultConfigOption())
	initWarnLogger(defaultConfigOption())
	initErrorLogger(defaultConfigOption())
	initPanicLogger(defaultConfigOption())
}

// InitWithConfig 传入自定义日志的配置选项
func InitWithConfig(cfg *config) {
	initDebugLogger(cfg)
	initInfoLogger(cfg)
	initWarnLogger(cfg)
	initErrorLogger(cfg)
	initPanicLogger(cfg)
}

// debug level logger
func initDebugLogger(cfg *config) {
	hook := getDebugLogWriter(cfg)
	writeSyncer := zapcore.NewMultiWriteSyncer(zapcore.AddSync(hook))
	encoder := getEncoder()
	var enab = new(zapcore.Level)
	err := enab.UnmarshalText([]byte(cfg.level))
	if err != nil {
		log.Fatal(err)
	}
	core := zapcore.NewCore(encoder, writeSyncer, enab)

	debugLogger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
}

func getDebugLogWriter(lc *config) io.Writer {
	fileName := fmt.Sprintf("%s%s", "debug", ".log")
	if len(lc.serviceName) > 0 {
		fileName = lc.serviceName + "_" + fileName
	}
	if len(lc.path) > 0 {
		fileName = lc.path + fileName
	}
	return getRotateLogs(fileName, lc.maxSize, lc.maxAge)
}

// info level logger
func initInfoLogger(cfg *config) {
	hook := getInfoLogWriter(cfg)
	writeSyncer := zapcore.NewMultiWriteSyncer(zapcore.AddSync(hook))
	encoder := getEncoder()
	var enab = new(zapcore.Level)
	err := enab.UnmarshalText([]byte(cfg.level))
	if err != nil {
		log.Fatal(err)
	}
	core := zapcore.NewCore(encoder, writeSyncer, enab)

	infoLogger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
}

func getInfoLogWriter(lc *config) io.Writer {
	fileName := fmt.Sprintf("%s%s", "info", ".log")
	if len(lc.serviceName) > 0 {
		fileName = lc.serviceName + "_" + fileName
	}
	if len(lc.path) > 0 {
		fileName = lc.path + fileName
	}
	return getRotateLogs(fileName, lc.maxSize, lc.maxAge)
}

// warn level logger
func initWarnLogger(cfg *config) {
	hook := getWarnLogWriter(cfg)
	writeSyncer := zapcore.NewMultiWriteSyncer(zapcore.AddSync(hook))
	encoder := getEncoder()
	var enab = new(zapcore.Level)
	err := enab.UnmarshalText([]byte(cfg.level))
	if err != nil {
		log.Fatal(err)
	}
	core := zapcore.NewCore(encoder, writeSyncer, enab)

	warnLogger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
}

func getWarnLogWriter(lc *config) io.Writer {
	fileName := fmt.Sprintf("%s%s", "warn", ".log")
	if len(lc.serviceName) > 0 {
		fileName = lc.serviceName + "_" + fileName
	}
	if len(lc.path) > 0 {
		fileName = lc.path + fileName
	}
	return getRotateLogs(fileName, lc.maxSize, lc.maxAge)
}

// error level logger
func initErrorLogger(cfg *config) {
	hook := getErrorLogWriter(cfg)
	writeSyncer := zapcore.NewMultiWriteSyncer(zapcore.AddSync(hook))
	encoder := getEncoder()
	var enab = new(zapcore.Level)
	err := enab.UnmarshalText([]byte(cfg.level))
	if err != nil {
		log.Fatal(err)
	}
	core := zapcore.NewCore(encoder, writeSyncer, enab)

	errorLogger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
}

func getErrorLogWriter(lc *config) io.Writer {
	fileName := fmt.Sprintf("%s%s", "error", ".log")
	if len(lc.serviceName) > 0 {
		fileName = lc.serviceName + "_" + fileName
	}
	if len(lc.path) > 0 {
		fileName = lc.path + fileName
	}
	return getRotateLogs(fileName, lc.maxSize, lc.maxAge)
}

// panic level logger
func initPanicLogger(cfg *config) {
	hook := getPanicLogWriter(cfg)
	writeSyncer := zapcore.NewMultiWriteSyncer(zapcore.AddSync(hook))
	encoder := getEncoder()
	var enab = new(zapcore.Level)
	err := enab.UnmarshalText([]byte(cfg.level))
	if err != nil {
		log.Fatal(err)
	}
	core := zapcore.NewCore(encoder, writeSyncer, enab)

	panicLogger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
}

func getPanicLogWriter(lc *config) io.Writer {
	fileName := fmt.Sprintf("%s%s", "panic", ".log")
	if len(lc.serviceName) > 0 {
		fileName = lc.serviceName + "_" + fileName
	}
	if len(lc.path) > 0 {
		fileName = lc.path + fileName
	}
	return getRotateLogs(fileName, lc.maxSize, lc.maxAge)
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.TimeKey = "time"
	encoderConfig.LevelKey = "level"
	encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
	encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
	return zapcore.NewJSONEncoder(encoderConfig)
}

func getRotateLogs(fileName string, size, age int) io.Writer {
	rotateLogs, err := rotatelogs.New(
		fileName+"."+time.Now().Local().Format("2006-01-02"),
		rotatelogs.WithRotationSize(int64(size)),
		rotatelogs.WithMaxAge(ageDate*time.Duration(age)),
		rotatelogs.WithRotationTime(splitDate*24))
	if err != nil {
		panic(any(err))
	}
	return rotateLogs
}
